import java.io.File;
import java.io.FileWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import com.github.javaparser.JavaParser;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.BooleanLiteralExpr;
import com.github.javaparser.ast.expr.Name;
import com.github.javaparser.ast.expr.SimpleName;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.visitor.ModifierVisitor;
import com.github.javaparser.ast.visitor.Visitable;
import com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter;

public class JavaCodeGenerator {
	/** This would allow us to keep exact formatting, but it's broken for FieldDeclarations for some reason. Potential related to
	 * https://github.com/javaparser/javaparser/issues/1601 */
	private static boolean LEXICAL_PRESERVING_PRINTER = false;

	private static File BASE_PROJECT_SRC = new File("../gdx-backend-robovm/src");

	private static Map<String, String> IMPORT_REPLACEMENTS = new HashMap<>();
	private static Map<String, String> CLASS_REPLACEMENTS = new HashMap<>();

	static {
		IMPORT_REPLACEMENTS.put("org.robovm.apple.opengles.EAGLContext",
			"com.badlogic.gdx.backends.iosrobovm.bindings.metalangle.MGLContext");
		IMPORT_REPLACEMENTS.put("org.robovm.apple.opengles.EAGLRenderingAPI",
			"com.badlogic.gdx.backends.iosrobovm.bindings.metalangle.MGLRenderingAPI");

		IMPORT_REPLACEMENTS.put("org.robovm.apple.glkit.GLKView",
			"com.badlogic.gdx.backends.iosrobovm.bindings.metalangle.MGLKView");
		IMPORT_REPLACEMENTS.put("org.robovm.apple.glkit.GLKViewController",
			"com.badlogic.gdx.backends.iosrobovm.bindings.metalangle.MGLKViewController");
		IMPORT_REPLACEMENTS.put("org.robovm.apple.glkit.GLKViewControllerDelegate",
			"com.badlogic.gdx.backends.iosrobovm.bindings.metalangle.MGLKViewControllerDelegate");
		IMPORT_REPLACEMENTS.put("org.robovm.apple.glkit.GLKViewDelegate",
			"com.badlogic.gdx.backends.iosrobovm.bindings.metalangle.MGLKViewDelegate");
		IMPORT_REPLACEMENTS.put("org.robovm.apple.glkit.GLKViewDrawableColorFormat",
			"com.badlogic.gdx.backends.iosrobovm.bindings.metalangle.MGLDrawableColorFormat");
		IMPORT_REPLACEMENTS.put("org.robovm.apple.glkit.GLKViewDrawableDepthFormat",
			"com.badlogic.gdx.backends.iosrobovm.bindings.metalangle.MGLDrawableDepthFormat");
		IMPORT_REPLACEMENTS.put("org.robovm.apple.glkit.GLKViewDrawableMultisample",
			"com.badlogic.gdx.backends.iosrobovm.bindings.metalangle.MGLDrawableMultisample");
		IMPORT_REPLACEMENTS.put("org.robovm.apple.glkit.GLKViewDrawableStencilFormat",
			"com.badlogic.gdx.backends.iosrobovm.bindings.metalangle.MGLDrawableStencilFormat");

		for (Entry<String, String> e : IMPORT_REPLACEMENTS.entrySet()) {
			String o = e.getKey().substring(e.getKey().lastIndexOf('.') + 1);
			String n = e.getValue().substring(e.getValue().lastIndexOf('.') + 1);
			CLASS_REPLACEMENTS.put(o, n);
		}
	}

	public static void main (String[] args) throws Exception {
		search(BASE_PROJECT_SRC);
	}

	private static void search (File f) throws Exception {
		if (f.isDirectory()) {
			for (File child : f.listFiles())
				search(child);
			return;
		}

		if (!f.getName().endsWith(".java")) {
			return;
		}

		parse(f);
	}

	private static void parse (File f) throws Exception {
		CompilationUnit cu = StaticJavaParser.parse(f);

		if (LEXICAL_PRESERVING_PRINTER) cu = LexicalPreservingPrinter.setup(cu);

		cu.setBlockComment("DO NOT EDIT THIS FILE - it is machine generated");

		final JavaParser javaParser = new JavaParser();

		ModifierVisitor<Object> visitor = new ModifierVisitor() {
			@Override
			public Name visit (Name n, final Object arg) {
				n = (Name)super.visit(n, arg);
				if (IMPORT_REPLACEMENTS.containsKey(n.asString())) {
					n = new Name(IMPORT_REPLACEMENTS.get(n.asString()));
				}
				return n;
			}

			@Override
			public Visitable visit (FieldDeclaration n, Object arg) {
				n = (FieldDeclaration)super.visit(n, arg);
				if (n.toString().contains("METALANGLE")) {
					for (VariableDeclarator variableDeclarator : n.getVariables()) {
						if (variableDeclarator.getName().asString().equals("IS_METALANGLE")) {
							variableDeclarator.setInitializer(new BooleanLiteralExpr(true));
						}
					}
				}
				return n;
			}

			@Override
			public Visitable visit (SimpleName n, Object arg) {
				n = (SimpleName)super.visit(n, arg);
				if (CLASS_REPLACEMENTS.containsKey(n.asString())) {
					n = new SimpleName(CLASS_REPLACEMENTS.get(n.asString()));
				}
				return n;
			}

			@Override
			public Visitable visit (ClassOrInterfaceType n, final Object arg) {
				n = (ClassOrInterfaceType)super.visit(n, arg);

				if (CLASS_REPLACEMENTS.containsKey(n.asString())) {
					n = javaParser.parseClassOrInterfaceType(CLASS_REPLACEMENTS.get(n.asString())).getResult().get();
				}
				return n;
			}

			@Override
			public Visitable visit (ClassOrInterfaceDeclaration n, Object arg) {
				n = (ClassOrInterfaceDeclaration)super.visit(n, arg);

				String base = n.getJavadocComment().isPresent() ? n.getJavadocComment().get().getContent() : "";

				n.setJavadocComment("DO NOT EDIT THIS FILE - it is machine generated\n" + base);

				if (n.getNameAsString().equals("IOSViewDelegate")) {
					for (MethodDeclaration m : n.getMethodsByName("willPause"))
						m.remove();
				}

				return n;
			}
		};

		cu.accept(visitor, null);

		File out = new File("src", f.getCanonicalPath().substring(BASE_PROJECT_SRC.getCanonicalPath().length()));
		try (FileWriter writer = new FileWriter(out)) {
			if (LEXICAL_PRESERVING_PRINTER)
				LexicalPreservingPrinter.print(cu, writer);
			else
				writer.append(cu.toString());
		}
	}
}
